package org.mindrive.system.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DateTimeUtil {
	private static final Logger logger = LoggerFactory.getLogger(DateTimeUtil.class);

	public static final String TIME_FORMAT_SHORT = "yyyyMMddHHmmss";
	public static final String TIME_FORMAT_YMD = "yyyy/MM/dd HH:mm:ss";
	public static final String TIME_FORMAT_NORMAL = "yyyy-MM-dd HH:mm:ss";
	public static final String TIME_FORMAT_ENGLISH = "MM/dd/yyyy HH:mm:ss";
	public static final String TIME_FORMAT_CHINA = "yyyy年MM月dd日 HH时mm分ss秒";
	public static final String TIME_FORMAT_CHINA_S = "yyyy年M月d日 H时m分s秒";
	public static final String TIME_FORMAT_SHORT_S = "HH:mm:ss";
	public static final String TIME_FORMAT_SHORT_SS = "HHmmss";
	public static final String TIME_FORMAT_SHORT_SSS = "yyyyMMddHHmmssS";
	public static final String DATE_FORMAT_SHORT = "yyyyMMdd";
	public static final String DATE_FORMAT_DOT = "yyyy.M.d";
	public static final String DATE_FORMAT_DOT_MONTH = "yyyy.MM";
	public static final String DATE_FORMAT_NORMAL = "yyyy-MM-dd";
	public static final String DATE_FORMAT_ENGLISH = "MM/dd/yyyy";
	public static final String DATE_FORMAT_CHINA = "yyyy年MM月dd日";
	public static final String DATE_FORMAT_CHINA_YEAR_MONTH = "yyyy年MM月";
	public static final String MONTH_FORMAT = "yyyyMM";
	public static final String YEAR_MONTH_FORMAT = "yyyy-MM";
	public static final String DATE_FORMAT_MINUTE = "yyyyMMddHHmm";
	public static final String MONTH_DAY_FORMAT = "MM-dd";
	public static final String TIME_FORMAT_NORMAL_24 = "yyyy-MM-dd HH:mm:ss";
	public static final String TIME_FORMAT_NORMAL_24_NO = "yyyyMMddHHmmss";
	public static final String TIME_FORMAT_YEAR = "yyyy";
	public static final String DATE_FORMAT_SIMPLE = "yyyy/M/d";
	public static final String DATE_FORMAT_FRONT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
	public static final String DATE_FORMAT_YYYY_DOT_MONTH = "yyyy.MM";
	public static final String DATE_FORMAT_TIKA = "yyyy-MM-dd'T'HH:mm:ss";

	/**
	 * 获取指定天的开始时间。
	 * 
	 * @param date 格式为yyyy-MM-dd
	 * @return
	 * @throws ParseException
	 */
	public static Date getStartTime(String date) throws ParseException {
		DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_NORMAL);
		Date inputDate = dateFormat.parse(date);

		Calendar dateCalendar = Calendar.getInstance();
		dateCalendar.setTime(inputDate);
		dateCalendar.set(Calendar.HOUR_OF_DAY, 0);
		dateCalendar.set(Calendar.MINUTE, 0);
		dateCalendar.set(Calendar.SECOND, 0);
		return dateCalendar.getTime();
	}

	public static Date getStartTime(Date date) {

		Calendar dateCalendar = Calendar.getInstance();
		dateCalendar.setTime(date);
		dateCalendar.set(Calendar.HOUR_OF_DAY, 0);
		dateCalendar.set(Calendar.MINUTE, 0);
		dateCalendar.set(Calendar.SECOND, 0);
		dateCalendar.set(Calendar.MILLISECOND, 0);
		return dateCalendar.getTime();
	}

	/**
	 * 获取指定天的结束时间。
	 * 
	 * @param date 格式为yyyy-MM-dd
	 * @return
	 * @throws ParseException
	 */
	public static Date getEndTime(String date) throws ParseException {
		DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_NORMAL);
		Date inputDate = dateFormat.parse(date);

		Calendar dateCalendar = Calendar.getInstance();
		dateCalendar.setTime(inputDate);
		dateCalendar.set(Calendar.HOUR_OF_DAY, 23);
		dateCalendar.set(Calendar.MINUTE, 59);
		dateCalendar.set(Calendar.SECOND, 59);
		return dateCalendar.getTime();
	}

	public static Date getEndTime(Date date) {

		Calendar dateCalendar = Calendar.getInstance();
		dateCalendar.setTime(date);
		dateCalendar.set(Calendar.HOUR_OF_DAY, 23);
		dateCalendar.set(Calendar.MINUTE, 59);
		dateCalendar.set(Calendar.SECOND, 59);
		dateCalendar.set(Calendar.MILLISECOND, 999);
		return dateCalendar.getTime();
	}

	/**
	 * 格式化日期类型
	 * 
	 * @param date
	 * @param format
	 * @return
	 */
	public static String format(Date date, String format) {
		DateFormat dateFormat = new SimpleDateFormat(format);
		return dateFormat.format(date);
	}

	public static Date parse(String date) {
		if (date == null || "".equals(date)) {
			return null;
		}
		try {
			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_FRONT);
			return dateFormat.parse(date);
		} catch (Exception ex) {
			logger.error("解析日期错误。格式：" + DATE_FORMAT_FRONT + ",参数：" + date);
		}
		try {
			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_TIKA);
			return dateFormat.parse(date);
		} catch (Exception ex) {
			logger.error("解析日期错误。格式：" + DATE_FORMAT_TIKA + ",参数：" + date);
		}

		try {
			DateFormat dateFormat = new SimpleDateFormat(TIME_FORMAT_NORMAL);
			return dateFormat.parse(date);
		} catch (Exception ex) {
			logger.error("解析日期错误。格式：" + TIME_FORMAT_NORMAL + ",参数：" + date);
		}

		try {
			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_NORMAL);
			return dateFormat.parse(date);
		} catch (Exception ex) {
			logger.error("解析日期错误。格式：" + DATE_FORMAT_NORMAL + ",参数：" + date);
		}

		try {
			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_CHINA);
			return dateFormat.parse(date);
		} catch (Exception ex) {
			logger.error("解析日期错误。格式：" + DATE_FORMAT_CHINA + ",参数：" + date);
		}

		try {
			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_CHINA_YEAR_MONTH);
			return dateFormat.parse(date);
		} catch (Exception ex) {
			logger.error("解析日期错误。格式：" + DATE_FORMAT_CHINA_YEAR_MONTH + ",参数：" + date);
		}
		try {
			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_YYYY_DOT_MONTH);
			return dateFormat.parse(date);
		} catch (Exception ex) {
			logger.error("解析日期错误。格式：" + DATE_FORMAT_YYYY_DOT_MONTH + ",参数：" + date);
		}

		try {
			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_SHORT);
			return dateFormat.parse(date);
		} catch (Exception ex) {
			logger.error("解析日期错误。格式：" + DATE_FORMAT_SHORT + ",参数：" + date);
		}
		try {
			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_SIMPLE);
			return dateFormat.parse(date);
		} catch (Exception ex) {
			logger.error("解析日期错误。格式：" + DATE_FORMAT_SIMPLE + ",参数：" + date);
		}

		return null;
	}

	public static Date parse(String date, String format) throws ParseException {
		DateFormat dateFormat = new SimpleDateFormat(format);
		return dateFormat.parse(date);
	}

	/**
	 * 返回两个日期之间的毫秒数的差距
	 * 
	 * @param d1 日期对象
	 * @param d2 日期对象
	 * @return 二者至1970年1.1后的毫秒数的差值
	 */
	public static long getMillisecondsOfTwoDate(Date d1, Date d2) {
		if (d1 == null || d2 == null) {
			throw new IllegalArgumentException("参数d1或d2不能是null对象！");
		}
		long dI1 = d1.getTime();
		long dI2 = d2.getTime();
		return (dI1 - dI2);
	}

	/**
	 * 获得两个日期之间相差的秒数
	 * 
	 * @param d1 日期对象
	 * @param d2 日期对象
	 * @return 两日期之间相差的秒数
	 */
	public static double getSecondsOfTwoDate(Date d1, Date d2) {
		if (d1 == null || d2 == null) {
			throw new IllegalArgumentException("参数d1或d2不能是null对象！");
		}
		long i = getMillisecondsOfTwoDate(d1, d2);

		return (double) i / 1000;
	}

	/**
	 * 获得两个日期之间相差的分钟数
	 * 
	 * @param d1 日期对象
	 * @param d2 日期对象
	 * @return 两日期之间相差的分钟数
	 */
	public static double getMinutesOfTwoDate(Date d1, Date d2) {
		if (d1 == null || d2 == null) {
			throw new IllegalArgumentException("参数d1或d2不能是null对象！");
		}
		long millions = getMillisecondsOfTwoDate(d1, d2);
		return (double) millions / (60 * 1000);
	}

	/**
	 * 获得两个日期之间相差的小时数
	 * 
	 * @param d1 日期对象
	 * @param d2 日期对象
	 * @return 两日期之间相差的小时数
	 */
	public static double getHoursOfTwoDate(Date d1, Date d2) {
		if (d1 == null || d2 == null) {
			throw new IllegalArgumentException("参数d1或d2不能是null对象！");
		}
		long millions = getMillisecondsOfTwoDate(d1, d2);
		return (double) millions / (1000 * 3600);
	}

	/**
	 * 获得两个日期之间相差的天数
	 * 
	 * @param d1 日期对象
	 * @param d2 日期对象
	 * @return 两日期之间相差的天数
	 */
	public static double getDaysOfTwoDate(Date d1, Date d2) {
		if (d1 == null || d2 == null) {
			throw new IllegalArgumentException("参数d1或d2不能是null对象！");
		}
		long millions = getMillisecondsOfTwoDate(d1, d2);
		return (double) millions / (1000 * 3600 * 24);
	}

	/**
	 * 获取两个日期之间所有的月份字符串(包含结束月）（yyyy-MM)
	 * 
	 * @param d1
	 * @param d2
	 * @return
	 */
	public static List<String> getMonthsStringOfTowDate(Date d1, Date d2) {
		List<String> months = new ArrayList<String>();

		DateFormat dateFormat = new SimpleDateFormat(YEAR_MONTH_FORMAT);
		Calendar startCalendar = Calendar.getInstance();
		Calendar endCalendar = Calendar.getInstance();
		// 将两个日期加载到Calendar中
		startCalendar.setTime(getMonthStartTime(d1));
		endCalendar.setTime(getMonthEndTime(d2));

		while (true) {
			months.add(dateFormat.format(startCalendar.getTime()));
			// 日期加1
			startCalendar.add(Calendar.MONTH, 1);
			// 当前日期和结束日历日期比较，超过结束日期则终止
			if (startCalendar.after(endCalendar)) {
				break;
			}

		}
		return months;
	}

	/**
	 * 获取指定年月的开始时间
	 * 
	 * @param year
	 * @param month
	 * @return
	 */
	public static Date getStartTime(int year, int month) {
		YearMonth yearMonth = YearMonth.of(year, month);
		LocalDate localDate = yearMonth.atDay(1);
		LocalDateTime startOfDay = localDate.atStartOfDay();
		ZonedDateTime zonedDateTime = startOfDay.atZone(ZoneId.of("Asia/Shanghai"));

		return Date.from(zonedDateTime.toInstant());
	}

	/**
	 * 获取指定年月的结束时间
	 * 
	 * @param year
	 * @param month
	 * @return
	 */
	public static Date getEndTime(int year, int month) {
		YearMonth yearMonth = YearMonth.of(year, month);
		LocalDate endOfMonth = yearMonth.atEndOfMonth();
		LocalDateTime localDateTime = endOfMonth.atTime(23, 59, 59, 999);
		ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai"));
		return Date.from(zonedDateTime.toInstant());
	}

	/**
	 * 取得季度的开始时间
	 * 
	 * @param date
	 * @return
	 */
	public static Date getQuarterStartTime(int year, int quarter) {
		int month = getBeginMonth(quarter);
		return getStartTime(year, month);

	}

	/**
	 * 取得季度的结束时间
	 * 
	 * @param year
	 * @param quarter
	 * @return
	 */
	public static Date getQuarterEndTime(int year, int quarter) {
		int month = getEndMonth(quarter);
		return getEndTime(year, month);
	}

	/**
	 * 取得年的第几周的开始时间
	 * 
	 * @param year
	 * @param week
	 * @return
	 */
	public static Date getWeekStartTime(int year, int week) {
		Calendar dateCalendar = Calendar.getInstance();
		dateCalendar.set(Calendar.YEAR, year);
		dateCalendar.set(Calendar.WEEK_OF_YEAR, week);
		dateCalendar.set(Calendar.DAY_OF_WEEK, 1);
		dateCalendar.set(Calendar.HOUR_OF_DAY, 0);
		dateCalendar.set(Calendar.MINUTE, 0);
		dateCalendar.set(Calendar.SECOND, 0);
		return dateCalendar.getTime();
	}

	/**
	 * 取得年的第几周的结束时间
	 * 
	 * @param year
	 * @param week
	 * @return
	 */
	public static Date getWeekEndTime(int year, int week) {
		Calendar dateCalendar = Calendar.getInstance();
		dateCalendar.set(Calendar.YEAR, year);
		dateCalendar.set(Calendar.WEEK_OF_YEAR, week);
		dateCalendar.set(Calendar.DAY_OF_WEEK, 7);
		dateCalendar.set(Calendar.HOUR_OF_DAY, 23);
		dateCalendar.set(Calendar.MINUTE, 59);
		dateCalendar.set(Calendar.SECOND, 59);
		dateCalendar.set(Calendar.MILLISECOND, 9999);
		return dateCalendar.getTime();
	}

	private static int getBeginMonth(int quarter) {
		int month = 0;
		switch (quarter) {
		case 1:
			month = 1;
		case 2:
			month = 4;
		case 3:
			month = 7;
		case 4:
			month = 10;
		default:
			month = 1;
		}
		return month;
	}

	private static int getEndMonth(int quarter) {
		int month = 0;
		switch (quarter) {
		case 1:
			month = 3;
		case 2:
			month = 6;
		case 3:
			month = 9;
		case 4:
			month = 12;
		default:
			month = 3;
		}
		return month;
	}

	/**
	 * 取得当前月的开始时间
	 * 
	 * @param date
	 * @return
	 */
	public static Date getMonthStartTime(Date date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
		return getStartTime(calendar.getTime());

	}

	/**
	 * 取得当前月的结束时间
	 * 
	 * @param date
	 * @return
	 */
	public static Date getMonthEndTime(Date date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
		return getEndTime(calendar.getTime());

	}

	/**
	 * 得到几天前的时间
	 * 
	 * @param d
	 * @param day
	 * @return
	 */
	public static Date getDateBefore(Date d, int day) {
		Calendar now = Calendar.getInstance();
		now.setTime(d);
		now.set(Calendar.DATE, now.get(Calendar.DATE) - day);
		return now.getTime();
	}

	/**
	 * 得到几天后的时间
	 * 
	 * @param d
	 * @param day
	 * @return
	 */
	public static Date getDateAfter(Date d, int day) {
		Calendar now = Calendar.getInstance();
		now.setTime(d);
		now.set(Calendar.DATE, now.get(Calendar.DATE) + day);
		return now.getTime();
	}

	public static int getCurrentYear(Date date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		return calendar.get(Calendar.YEAR);
	}

	public static int getYear(Date date, int amount) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.YEAR, amount);
		return calendar.get(Calendar.YEAR);
	}

	/**
	 * 取得本月开始时间
	 * 
	 * @return
	 */
	public static Date getMonthStartTime() {

		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
		return getStartTime(calendar.getTime());

	}

	/**
	 * 取得本月结束时间
	 * 
	 * @return
	 */
	public static Date getMonthEndTime() {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
		return getEndTime(calendar.getTime());

	}

	/**
	 * 取得本年开始时间
	 * 
	 * @return
	 */
	public static Date getYearStartTime() {

		Calendar calendar = Calendar.getInstance();
		Date now = new Date();

		calendar.set(Calendar.YEAR, getCurrentYear(now));

		calendar.set(Calendar.MONTH, Calendar.JANUARY);

		calendar.set(Calendar.DATE, 1);

		return getStartTime(calendar.getTime());

	}

	/**
	 * 取得本年的结束时间
	 * 
	 * @return
	 */
	public static Date getYearEndTime() {

		Calendar cal = Calendar.getInstance();

		Date now = new Date();

		cal.set(Calendar.YEAR, getCurrentYear(now));

		cal.set(Calendar.MONTH, Calendar.DECEMBER);

		cal.set(Calendar.DATE, 31);

		return getEndTime(cal.getTime());

	}

	/**
	 * 取得本周的开始时间
	 * 
	 * @return
	 */
	public static Date getWeekStartTime() {

		Date date = new Date();

		Calendar cal = Calendar.getInstance();

		cal.setTime(date);

		int dayofweek = cal.get(Calendar.DAY_OF_WEEK);

		if (dayofweek == 1) {

			dayofweek += 7;

		}

		cal.add(Calendar.DATE, 2 - dayofweek);

		return getStartTime(cal.getTime());

	}

	/**
	 * 取得本周的结束时间
	 * 
	 * @return
	 */
	public static Date getWeekEndTime() {

		Calendar cal = Calendar.getInstance();

		cal.setTime(getWeekStartTime());

		cal.add(Calendar.DAY_OF_WEEK, 6);

		Date weekEndSta = cal.getTime();

		return getEndTime(weekEndSta);

	}
	
	/**
	 * 
	 * 计算某年某周的开始日期
	 *
	 * 
	 * 
	 * @param yearNum 格式 yyyy， 必须大于1900年度 小于9999年
	 *
	 * 
	 * 
	 * @param weekNum 1到52或者53
	 * 
	 * @return 日期，格式为yyyy-MM-dd
	 * 
	 */

	public static String getYearWeekFirstDay(int yearNum, int weekNum) {
		if (yearNum < 1900 || yearNum > 9999) {
			throw new NullPointerException("年度必须大于等于1900年小于等于9999年");
		}

		Calendar cal = Calendar.getInstance();

		cal.setFirstDayOfWeek(Calendar.MONDAY); // 设置每周的第一天为星期一

		cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);// 每周从周一开始

		// 上面两句代码配合，才能实现，每年度的第一个周，是包含第一个星期一的那个周。

		cal.setMinimalDaysInFirstWeek(7); // 设置每周最少为7天

		cal.set(Calendar.YEAR, yearNum);

		cal.set(Calendar.WEEK_OF_YEAR, weekNum);

		// 分别取得当前日期的年、月、日

		return format(cal.getTime(), DATE_FORMAT_NORMAL);

	}

	/**
	 * 
	 * 计算指定年度共有多少个周。
	 * 
	 * @param year 格式 yyyy ，必须大于1900年度 小于9999年
	 * 
	 * @return
	 * 
	 */

	public static int getWeekNumByYear(final int year) {
		if (year < 1900 || year > 9999) {
			throw new NullPointerException("年度必须大于等于1900年小于等于9999年");

		}

		int result = 52;// 每年至少有52个周 ，最多有53个周。

		String date = getYearWeekFirstDay(year, 53);

		if (date.substring(0, 4).equals(year + "")) { // 判断年度是否相符，如果相符说明有53个周。

			result = 53;

		}

		return result;

	}
}
